package com.ssy.lingxi.order.serviceimpl.platform;

import com.ssy.lingxi.common.constant.basic.EnableDisableStatus;
import com.ssy.lingxi.common.constant.order.OrderTradeProcessTypeEnum;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.response.PageData;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.order.entity.BaseTradeProcessDO;
import com.ssy.lingxi.order.entity.PlatformTradeProcessDO;
import com.ssy.lingxi.order.entity.PlatformTradeProcessMemberDO;
import com.ssy.lingxi.order.model.bo.PayNodeBO;
import com.ssy.lingxi.order.model.constant.BaseTradeProcessEnum;
import com.ssy.lingxi.order.model.constant.OrderServiceContants;
import com.ssy.lingxi.order.model.dto.OrderMemberQueryDTO;
import com.ssy.lingxi.order.model.vo.basic.request.OrderProcessIdVO;
import com.ssy.lingxi.order.model.vo.basic.request.OrderProcessUpdateStatusVO;
import com.ssy.lingxi.order.model.vo.platform.request.PlatformProcessMemberPageVO;
import com.ssy.lingxi.order.model.vo.platform.request.PlatformProcessUpdateVO;
import com.ssy.lingxi.order.model.vo.platform.request.PlatformProcessVO;
import com.ssy.lingxi.order.model.vo.platform.request.PlatfromProcessPageVO;
import com.ssy.lingxi.order.model.vo.platform.response.PlatformBaseTradeProcessVO;
import com.ssy.lingxi.order.model.vo.platform.response.PlatformProcessDetailVO;
import com.ssy.lingxi.order.model.vo.platform.response.PlatformProcessPageQueryVO;
import com.ssy.lingxi.order.model.vo.process.response.BaseTradeProcessVO;
import com.ssy.lingxi.order.model.vo.process.response.OrderTradeProcessPaymentDetailGroupVO;
import com.ssy.lingxi.order.model.vo.process.response.OrderTradeProcessPaymentDetailVO;
import com.ssy.lingxi.order.repository.PlatformTradeProcessRepository;
import com.ssy.lingxi.order.service.base.IBaseCacheService;
import com.ssy.lingxi.order.service.base.IBasePlatformTradeProcessMemberService;
import com.ssy.lingxi.order.service.base.IBaseTradeProcessService;
import com.ssy.lingxi.order.service.platform.IPlatformTradeProcessService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 平台后台 - 交易规则配置相关接口
 * @author 万宁
 * @version 2.0.0
 * @date 2021-07-26
 */
@Service
public class PlatformTradeProcessServiceImpl implements IPlatformTradeProcessService {
    @Resource
    private IBaseCacheService baseCacheService;

    @Resource
    private IBaseTradeProcessService baseTradeProcessService;

    @Resource
    private IBasePlatformTradeProcessMemberService basePlatformTradeProcessMemberService;

    @Resource
    private PlatformTradeProcessRepository platformTradeProcessRepository;


    /**
     * 分页查询交易流程规则配置
     *
     * @param headers HttpHeaders信息
     * @param pageVO  接口参数
     * @return 查询结果
     */
    @Override
    public Wrapper<PageData<PlatformProcessPageQueryVO>> pageTradeProcess(HttpHeaders headers, PlatfromProcessPageVO pageVO) {
        baseCacheService.needLoginFromManagePlatform(headers);
        Pageable pageable = PageRequest.of(pageVO.getCurrent() - 1, pageVO.getPageSize(), Sort.by("createTime").descending());
        Specification<PlatformTradeProcessDO> specification = (Specification<PlatformTradeProcessDO>) (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            if(StringUtils.hasLength(pageVO.getName())) {
                list.add(criteriaBuilder.like(root.get("name").as(String.class), "%" + pageVO.getName().trim() + "%"));
            }
            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        Page<PlatformTradeProcessDO> pageList = platformTradeProcessRepository.findAll(specification, pageable);
        return Wrapper.success(new PageData<>(pageList.getTotalElements(), pageList.getContent().stream().map(process ->{
            PlatformProcessPageQueryVO queryVO = new PlatformProcessPageQueryVO();
            queryVO.setProcessId(process.getId());
            queryVO.setCreateTime(process.getCreateTime().format(OrderServiceContants.DEFAULT_TIME_FORMATTER));
            queryVO.setName(process.getName());
            queryVO.setStatus(process.getStatus());
            queryVO.setStatusName(EnableDisableStatus.getNameByCode(process.getStatus()));
            return queryVO;
        }).collect(Collectors.toList())));
    }

    /**
     * 新增交易规则页面 - 查询基础交易流程列表
     *
     * @param headers Http头部信息
     * @return 查询结果
     */
    @Override
    public Wrapper<List<PlatformBaseTradeProcessVO>> listBaseTradeProcess(HttpHeaders headers) {
        baseCacheService.needLoginFromManagePlatform(headers);
        return Wrapper.success(baseTradeProcessService.listProcessByPlatform());
    }

    /**
     * 新增交易流程规则
     *
     * @param headers   HttpHeaders信息
     * @param processVO 接口参数
     * @return 新增结果
     */
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public Wrapper<Void> createTradeProcess(HttpHeaders headers, PlatformProcessVO processVO) {
        baseCacheService.needLoginFromManagePlatform(headers);

        //Step 1: 判断基础流程
        BaseTradeProcessDO baseTradeProcess = baseTradeProcessService.findById(processVO.getBaseProcessId());
        if(baseTradeProcess == null) {
            return Wrapper.fail(ResponseCode.ORDER_BASE_TRADE_PROCESS_DOES_NOT_EXIST);
        }

        //Step 2: 判断是否已经存在
        if(platformTradeProcessRepository.existsByProcess(baseTradeProcess)) {
            return Wrapper.fail(ResponseCode.ORDER_PLATFORM_TRADE_PROCESS_EXISTS);
        }

        //Step 3: 新增
        PlatformTradeProcessDO tradeProcess = new PlatformTradeProcessDO();
        tradeProcess.setCreateTime(LocalDateTime.now());
        tradeProcess.setName(processVO.getName());
        tradeProcess.setProcess(baseTradeProcess);
        tradeProcess.setProcessKey(baseTradeProcess.getProcessKey());
        tradeProcess.setProcessType(baseTradeProcess.getProcessType());
        tradeProcess.setProcessKind(baseTradeProcess.getProcessKind());
        tradeProcess.setSkipFirstStep(baseTradeProcess.getSkipFirstStep());
        tradeProcess.setPayTimes(baseTradeProcess.getPayTimes());
        tradeProcess.setStatus(EnableDisableStatus.ENABLE.getCode());
        tradeProcess.setAllMembers(processVO.getAllMembers());
        //由于关联实体时 CascadeType.DETACH, 所以要先保存一次
        platformTradeProcessRepository.saveAndFlush(tradeProcess);

        // 3-1 : 校验、保存关联的会员
        Wrapper<Void> checkResult = basePlatformTradeProcessMemberService.checkMembers(tradeProcess, processVO.getAllMembers(), processVO.getMembers());
        if(checkResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Wrapper.fail(checkResult.getCode(), checkResult.getMessage());
        }

        platformTradeProcessRepository.saveAndFlush(tradeProcess);
        return Wrapper.success();
    }

    /**
     * 查询交易流程规则详情
     *
     * @param headers HttpHeaders信息
     * @param idVO    接口参数
     * @return 查询结果
     */
    @Override
    public Wrapper<PlatformProcessDetailVO> getTradeProcess(HttpHeaders headers, OrderProcessIdVO idVO) {
        PlatformTradeProcessDO tradeProcess = platformTradeProcessRepository.findById(idVO.getProcessId()).orElse(null);
        if(tradeProcess == null) {
            return Wrapper.fail(ResponseCode.ORDER_PLATFORM_TRADE_PROCESS_DOES_NOT_EXIST);
        }

        PlatformProcessDetailVO detailVO = new PlatformProcessDetailVO();
        detailVO.setProcessId(tradeProcess.getId());
        detailVO.setName(tradeProcess.getName());
        detailVO.setBaseProcessId(tradeProcess.getProcess().getId());
        detailVO.setAllMembers(tradeProcess.getAllMembers());
        detailVO.setStatus(tradeProcess.getStatus());
        detailVO.setStatusName(EnableDisableStatus.getNameByCode(tradeProcess.getStatus()));
        return Wrapper.success(detailVO);
    }

    /**
     * 分页查询交易流程规则适用会员列表
     *
     * @param headers HttpHeaders信息
     * @param pageVO  接口参数
     * @return 查询结果
     */
    @Override
    public Wrapper<PageData<OrderMemberQueryDTO>> pageTradeProcessMembers(HttpHeaders headers, PlatformProcessMemberPageVO pageVO) {
        baseCacheService.needLoginFromManagePlatform(headers);
        PlatformTradeProcessDO tradeProcess = platformTradeProcessRepository.findById(pageVO.getProcessId()).orElse(null);
        if(tradeProcess == null) {
            return Wrapper.fail(ResponseCode.ORDER_PLATFORM_TRADE_PROCESS_DOES_NOT_EXIST);
        }

        return basePlatformTradeProcessMemberService.pageMembers(tradeProcess, pageVO.getName(), pageVO.getCurrent(), pageVO.getPageSize());
    }

    /**
     * 修改交易流程规则
     *
     * @param headers  HttpHeaders信息
     * @param updateVO 接口参数
     * @return 修改结果
     */
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public Wrapper<Void> updateTradeProcess(HttpHeaders headers, PlatformProcessUpdateVO updateVO) {
        baseCacheService.needLoginFromManagePlatform(headers);
        PlatformTradeProcessDO tradeProcess = platformTradeProcessRepository.findById(updateVO.getProcessId()).orElse(null);
        if(tradeProcess == null) {
            return Wrapper.fail(ResponseCode.ORDER_PLATFORM_TRADE_PROCESS_DOES_NOT_EXIST);
        }

        //Step 1: 判断基础流程
        BaseTradeProcessDO baseTradeProcess = baseTradeProcessService.findById(updateVO.getBaseProcessId());
        if(baseTradeProcess == null) {
            return Wrapper.fail(ResponseCode.ORDER_BASE_TRADE_PROCESS_DOES_NOT_EXIST);
        }

        //Step 2: 判断是否已经存在
        if(platformTradeProcessRepository.existsByProcessAndIdNot(baseTradeProcess, tradeProcess.getId())) {
            return Wrapper.fail(ResponseCode.ORDER_PLATFORM_TRADE_PROCESS_EXISTS);
        }

        //Step 3: 修改
        tradeProcess.setCreateTime(LocalDateTime.now());
        tradeProcess.setName(updateVO.getName());
        tradeProcess.setProcess(baseTradeProcess);
        tradeProcess.setProcessKey(baseTradeProcess.getProcessKey());
        tradeProcess.setProcessType(baseTradeProcess.getProcessType());
        tradeProcess.setProcessKind(baseTradeProcess.getProcessKind());
        tradeProcess.setSkipFirstStep(baseTradeProcess.getSkipFirstStep());
        tradeProcess.setPayTimes(baseTradeProcess.getPayTimes());
        tradeProcess.setAllMembers(updateVO.getAllMembers());

        // 3-1 : 校验、修改关联的会员
        Wrapper<Void> checkResult = basePlatformTradeProcessMemberService.updateMembers(tradeProcess, updateVO.getAllMembers(), updateVO.getMembers());
        if(checkResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Wrapper.fail(checkResult.getCode(), checkResult.getMessage());
        }

        platformTradeProcessRepository.saveAndFlush(tradeProcess);
        return Wrapper.success();
    }

    /**
     * 修改交易流程规则状态
     *
     * @param headers  HttpHeaders信息
     * @param statusVO 接口参数
     * @return 修改结果
     */
    @Override
    public Wrapper<Void> updateTradeProcessStatus(HttpHeaders headers, OrderProcessUpdateStatusVO statusVO) {
        baseCacheService.needLoginFromManagePlatform(headers);
        PlatformTradeProcessDO tradeProcess = platformTradeProcessRepository.findById(statusVO.getProcessId()).orElse(null);
        if(tradeProcess == null) {
            return Wrapper.fail(ResponseCode.ORDER_PLATFORM_TRADE_PROCESS_DOES_NOT_EXIST);
        }

        tradeProcess.setCreateTime(LocalDateTime.now());
        tradeProcess.setStatus(statusVO.getStatus());
        platformTradeProcessRepository.saveAndFlush(tradeProcess);
        return Wrapper.success();
    }

    /**
     * 删除交易流程规则
     *
     * @param headers HttpHeaders信息
     * @param idVO    接口参数
     * @return 删除结果
     */
    @Override
    public Wrapper<Void> deleteTradeProcess(HttpHeaders headers, OrderProcessIdVO idVO) {
        baseCacheService.needLoginFromManagePlatform(headers);
        PlatformTradeProcessDO tradeProcess = platformTradeProcessRepository.findById(idVO.getProcessId()).orElse(null);
        if(tradeProcess == null) {
            return Wrapper.fail(ResponseCode.ORDER_PLATFORM_TRADE_PROCESS_DOES_NOT_EXIST);
        }

        platformTradeProcessRepository.delete(tradeProcess);
        return Wrapper.success();
    }

    /**
     * 系统能力 - 新增交易流程规则配置时，根据平台后台的配置查询基础交易流程列表
     *
     * @param memberId 会员Id
     * @param roleId   会员角色Id
     * @return 查询结果
     */
    @Override
    public List<BaseTradeProcessVO> listOrderTradeProcess(Long memberId, Long roleId) {
        Specification<PlatformTradeProcessDO> specification = (Specification<PlatformTradeProcessDO>) (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.isTrue(root.get("allMembers").as(Boolean.class)));

            Join<PlatformTradeProcessDO, PlatformTradeProcessMemberDO> memberJoin = root.join("members", JoinType.LEFT);
            list.add(criteriaBuilder.and(criteriaBuilder.equal(memberJoin.get("memberId").as(Long.class), memberId), criteriaBuilder.equal(memberJoin.get("roleId").as(Long.class), roleId)));

            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.or(list.toArray(p));
        };

        return platformTradeProcessRepository.findAll(specification).stream().filter(tradeProcess -> tradeProcess.getStatus().equals(EnableDisableStatus.ENABLE.getCode())).map(PlatformTradeProcessDO::getProcess).sorted(Comparator.comparingLong(BaseTradeProcessDO::getId)).map(process -> {
            BaseTradeProcessVO processVO = new BaseTradeProcessVO();
            processVO.setBaseProcessid(process.getId());
            processVO.setProcessName(BaseTradeProcessEnum.getNameByCode(process.getCode()));
            processVO.setProcessType(process.getProcessType());
            processVO.setProcessTypeName(OrderTradeProcessTypeEnum.getNameByCode(process.getProcessType()));
            processVO.setDescription(BaseTradeProcessEnum.getRemarkByCode(process.getCode()));
            processVO.setPayTimes(process.getPayTimes());
            processVO.setPayments(CollectionUtils.isEmpty(process.getPayNodes()) ? new ArrayList<>() : process.getPayNodes().stream().collect(Collectors.groupingBy(PayNodeBO::getSerialNo)).entrySet().stream().map(entry -> {
                OrderTradeProcessPaymentDetailGroupVO groupVO = new OrderTradeProcessPaymentDetailGroupVO();
                groupVO.setSerialNo(entry.getKey());
                groupVO.setNodes(entry.getValue().stream().map(node -> {
                    OrderTradeProcessPaymentDetailVO nodeVO = new OrderTradeProcessPaymentDetailVO();
                    nodeVO.setBatchNo(node.getBatchNo());
                    nodeVO.setPayNode(node.getPayNode());
                    nodeVO.setPayRate("");
                    return nodeVO;
                }).sorted(Comparator.comparingInt(OrderTradeProcessPaymentDetailVO::getBatchNo)).collect(Collectors.toList()));
                return groupVO;
            }).sorted(Comparator.comparingInt(OrderTradeProcessPaymentDetailGroupVO::getSerialNo)).collect(Collectors.toList()));
            return processVO;
        }).collect(Collectors.toList());
    }
}
